#pragma once
#include <string>
#include <iostream>
#include <cstdarg>
#include <fstream>
#include <unistd.h>
#include <cstring>
enum grade
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
struct log_message
{
    std::string _level;
    pid_t _id;
    std::string _filename;
    int _filenumber;
    std::string _cur_time;
    std::string _message_info;
};

std::string GradeToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case INFO:
        return "INFO";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "UNKNOWN";
    }
}

std::string GetCurTime()
{
    const time_t t = time(nullptr);
    struct tm *cur_time = localtime(&t);
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",
             cur_time->tm_year + 1900,
             cur_time->tm_mon + 1,
             cur_time->tm_mday,
             cur_time->tm_hour,
             cur_time->tm_min,
             cur_time->tm_sec);
    return buffer;
}

const std::string default_logfile = "./log.txt";
#define SCREEN_TYPE 1
#define FILE_TYPE 2
pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

class log
{
public:
    log(const std::string &logfile = default_logfile)
        : _log_file(logfile), _type(SCREEN_TYPE)
    {
    }
    void ModifyType(int type)
    {
        _type = type;
    }

    void logMessage(const std::string &filename, int filenumber, int level, const char *format, ...)
    {
        log_message lg;

        lg._level = GradeToString(level);
        lg._id = getpid();
        lg._filenumber = filenumber;
        lg._filename = filename;
        lg._cur_time = GetCurTime();

        va_list va;
        va_start(va, format);
        char log_info[1024];
        vsnprintf(log_info, sizeof(log_info), format, va);
        va_end(va);
        lg._message_info = log_info;
        // 开始打印日志
        FlushLog(lg);
    }
    void FlushLog(log_message &lg)
    {
        pthread_mutex_lock(&gmutex);
        if (_type == SCREEN_TYPE)
            FlushScreen(lg);
        else
            FlushFile(lg);
        pthread_mutex_unlock(&gmutex);
    }
    void FlushScreen(log_message &lg)
    {
        printf("[%s][%d][%s][%d][%s] %s",
               lg._level.c_str(),
               lg._id,
               lg._filename.c_str(),
               lg._filenumber,
               lg._cur_time.c_str(),
               lg._message_info.c_str());
    }
    void FlushFile(log_message &lg)
    {
        std::ofstream out(_log_file, std::ios::app);
        if (!out.is_open())
            return;
        char logtxt[2048];
        snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",
                 lg._level.c_str(),
                 lg._id,
                 lg._filename.c_str(),
                 lg._filenumber,
                 lg._cur_time.c_str(),
                 lg._message_info.c_str());
        out.write(logtxt, strlen(logtxt));
        out.close();
    }

private:
    int _type;
    std::string _log_file;
};

log lg;

#define LOG(Level, Format, ...)                                        \
    do                                                                 \
    {                                                                  \
        lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \
    } while (0)
#define EnableScreen()          \
    do                          \
    {                           \
        lg.ModifyType(SCREEN_TYPE); \
    } while (0)
#define EnableFILE()          \
    do                        \
    {                         \
        lg.ModifyType(FILE_TYPE); \
    } while (0)